package com.vict.task.utils.lock;

import com.vict.task.config.LocalTaskCommon;
import lombok.Data;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

public interface LocalTaskLockApi {

    class LockThrowException extends RuntimeException{ }
    class NoLockThrowException extends RuntimeException{ }

    String getLockKey();

    void lockBlock();

    void unlockIfSuccess();

    boolean lockNotBlock();

    static void unlockIfSuccess(LocalTaskLockApi localTaskLockApi){
        Optional.ofNullable(localTaskLockApi).ifPresent(o-> o.unlockIfSuccess());
    }

    static LocalTaskLockApi getLock(String lockKey){
        if(LocalTaskCommon.lockImpl.equals(LocalTaskCommon.lockImpl_jdk)){
            return LocalTaskLockJdk.getLock(LocalTaskCommon.lockPrx + lockKey);
        }else if(LocalTaskCommon.lockImpl.equals(LocalTaskCommon.lockImpl_redis)){
            return RedisLocalTaskLockDynamic.getLock(LocalTaskCommon.lockPrx + lockKey);
        }
        throw new NoLockThrowException();
    }

    static LocalTaskLockApi getLockByType(String lockKey, String lockTypeStr){
        String lockType = lockTypeStr;
        if(lockType == null){
            lockType = LocalTaskCommon.lockImpl;
        }

        if(lockType.equals(LocalTaskCommon.lockImpl_jdk)){
            return LocalTaskLockJdk.getLock(LocalTaskCommon.lockPrx + lockKey);
        }else if(lockType.equals(LocalTaskCommon.lockImpl_redis)){
            return RedisLocalTaskLockDynamic.getLock(LocalTaskCommon.lockPrx + lockKey);
        }
        throw new NoLockThrowException();
    }

    /**
     * 强制指定报错时间
     */
    static LocalTaskLockApi getLockOverThrowException(String lockKey, Long overSeconds){
        if(LocalTaskCommon.lockImpl.equals(LocalTaskCommon.lockImpl_jdk)){
            return LocalTaskLockJdk.getLockOverThrowException(LocalTaskCommon.lockPrx + lockKey, overSeconds);
        }else if(LocalTaskCommon.lockImpl.equals(LocalTaskCommon.lockImpl_redis)){
            return RedisLocalTaskLockDynamic.getLockOverThrowException(LocalTaskCommon.lockPrx + lockKey, overSeconds);
        }
        throw new NoLockThrowException();
    }

    /**
     * 强制指定报错时间
     */
    static LocalTaskLockApi getLockOverThrowExceptionByType(String lockKey, Long overSeconds, String lockTypeStr){
        String lockType = lockTypeStr;
        if(lockType == null){
            lockType = LocalTaskCommon.lockImpl;
        }

        if(lockType.equals(LocalTaskCommon.lockImpl_jdk)){
            return LocalTaskLockJdk.getLockOverThrowException(LocalTaskCommon.lockPrx + lockKey, overSeconds);
        }else if(lockType.equals(LocalTaskCommon.lockImpl_redis)){
            return RedisLocalTaskLockDynamic.getLockOverThrowException(LocalTaskCommon.lockPrx + lockKey, overSeconds);
        }
        throw new NoLockThrowException();
    }

    /**
     * 强制指定过期时间
     */
    static LocalTaskLockApi getLockAutoRelease(String lockKey, Long autoReleaseSeconds){
        if(LocalTaskCommon.lockImpl.equals(LocalTaskCommon.lockImpl_jdk)){
            return LocalTaskLockJdk.getLockAutoRelease(LocalTaskCommon.lockPrx + lockKey, autoReleaseSeconds);
        }else if(LocalTaskCommon.lockImpl.equals(LocalTaskCommon.lockImpl_redis)){
            return RedisLocalTaskLockDynamic.getLockAutoRelease(LocalTaskCommon.lockPrx + lockKey, autoReleaseSeconds);
        }
        throw new NoLockThrowException();
    }

    /**
     * 强制指定过期时间
     */
    static LocalTaskLockApi getLockAutoReleaseByType(String lockKey, Long autoReleaseSeconds, String lockTypeStr){

        String lockType = lockTypeStr;
        if(lockType == null){
            lockType = LocalTaskCommon.lockImpl;
        }

        if(lockType.equals(LocalTaskCommon.lockImpl_jdk)){
            return LocalTaskLockJdk.getLockAutoRelease(LocalTaskCommon.lockPrx + lockKey, autoReleaseSeconds);
        }else if(lockType.equals(LocalTaskCommon.lockImpl_redis)){
            return RedisLocalTaskLockDynamic.getLockAutoRelease(LocalTaskCommon.lockPrx + lockKey, autoReleaseSeconds);
        }
        throw new NoLockThrowException();
    }

    /**
     * 强制指定过期时间和报错时间
     */
    static LocalTaskLockApi getLockAutoReleaseAndOverThrowException(String lockKey, Long autoReleaseSeconds, Long overSeconds){
        if(LocalTaskCommon.lockImpl.equals(LocalTaskCommon.lockImpl_jdk)){
            return LocalTaskLockJdk.getLockAutoReleaseAndOverThrowException(LocalTaskCommon.lockPrx + lockKey, autoReleaseSeconds, overSeconds);
        }else if(LocalTaskCommon.lockImpl.equals(LocalTaskCommon.lockImpl_redis)){
            return RedisLocalTaskLockDynamic.getLockAutoReleaseAndOverThrowException(LocalTaskCommon.lockPrx + lockKey, autoReleaseSeconds, overSeconds);
        }
        throw new NoLockThrowException();
    }

    /**
     * 强制指定过期时间和报错时间
     */
    static LocalTaskLockApi getLockAutoReleaseAndOverThrowExceptionByType(String lockKey, Long autoReleaseSeconds, Long overSeconds, String lockTypeStr){

        String lockType = lockTypeStr;
        if(lockType == null){
            lockType = LocalTaskCommon.lockImpl;
        }

        if(lockType.equals(LocalTaskCommon.lockImpl_jdk)){
            return LocalTaskLockJdk.getLockAutoReleaseAndOverThrowException(LocalTaskCommon.lockPrx + lockKey, autoReleaseSeconds, overSeconds);
        }else if(lockType.equals(LocalTaskCommon.lockImpl_redis)){
            return RedisLocalTaskLockDynamic.getLockAutoReleaseAndOverThrowException(LocalTaskCommon.lockPrx + lockKey, autoReleaseSeconds, overSeconds);
        }
        throw new NoLockThrowException();
    }

    static LockList getLockList(LocalTaskLockApi...locks){
        LockList lockList = new LockList();

        List<LocalTaskLockApi> redisLockDynamics = Optional.ofNullable(locks).filter(o -> o.length > 0).map(o -> Arrays.asList(o)).orElse(new ArrayList<>());
        lockList.setLockList(redisLockDynamics);

        return lockList;
    }

    @Data
    static class LockList {
        private List<LocalTaskLockApi> lockList;

        public void addLock(LocalTaskLockApi lock){
            Optional.ofNullable(this.lockList).ifPresent(o-> o.add(lock));
        }

        public void lockBlock(){
            if(this.lockList == null || this.lockList.size() <= 0){
                return;
            }
            this.lockList = this.lockList.stream().collect(Collectors.toSet()).stream().collect(Collectors.toList());

            this.lockList.sort((o1, o2) -> {
                if(o1.getLockKey().compareTo(o2.getLockKey()) < 0){
                    return -1;
                }else if(o1.getLockKey().compareTo(o2.getLockKey()) > 0){
                    return 1;
                }else{
                    return 0;
                }
            });

            for(LocalTaskLockApi lock : this.lockList){
                lock.lockBlock();
            }
        }

        public void unlockIfSuccess() {
            this.lockList.sort((o1, o2) -> {
                if(o1.getLockKey().compareTo(o2.getLockKey()) < 0){
                    return -1;
                }else if(o1.getLockKey().compareTo(o2.getLockKey()) > 0){
                    return 1;
                }else{
                    return 0;
                }
            });
            for(LocalTaskLockApi lock : this.lockList){
                lock.unlockIfSuccess();
            }
        }
    }
}
